package com.lipaluma.excel.io;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Workbook;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;

import com.lipaluma.exceptions.CsvParserException;
import com.lipaluma.field.mapper.mapping.Column;
import com.lipaluma.field.mapper.mapping.MappingInfo;
import com.lipaluma.field.mapper.single.impl.DateTimeFieldMapper;
import com.lipaluma.io.Row;
/**
 * Row from a Excel File
 * @author Mario
 */
public class ExcelRow implements Row {

	private final org.apache.poi.ss.usermodel.Row row;
	private final Workbook workbook;

	/**
	 * Create a row reader/writer for an Excel line
	 * @param workbook workbook of the excel file
	 * @param row the excel row
	 */
	public ExcelRow(Workbook workbook, org.apache.poi.ss.usermodel.Row row) {
		this.workbook = workbook;
		this.row = row;
	}
	
	public String extractValue(MappingInfo info) {
		Column columnInfo = (Column)info;
		Cell cell = row.getCell(columnInfo.getColumnIndex());
		return extractValueFromCell(cell);
	}

	private String extractValueFromCell(Cell cell) {
		if(cell == null)
			return null;
		switch (cell.getCellType()) {
		case Cell.CELL_TYPE_BLANK:
			return null;
		case Cell.CELL_TYPE_BOOLEAN:
			return String.valueOf(cell.getBooleanCellValue());
		case Cell.CELL_TYPE_NUMERIC:
			if (DateUtil.isCellDateFormatted(cell))
				return new DateTime(cell.getDateCellValue()).toString(DateTimeFormat.forPattern("yyyy-MM-dd"));
			return String.valueOf(cell.getNumericCellValue());
		case Cell.CELL_TYPE_STRING:
			return cell.getRichStringCellValue().toString();
		case Cell.CELL_TYPE_FORMULA : 
			return evaluateFormula(cell);
		default :
			throw new CsvParserException("The value cannot be extracted from the excel file on row index "+row.getRowNum()+" and column index "+cell.getColumnIndex());
		}
	}

	@Override
	public void writeValue(Object value, MappingInfo info) {
		Column columnInfo = (Column)info;
		int cellType = getCellTypeFromValue(value);
		Cell cell = row.createCell(columnInfo.getColumnIndex(), cellType);
		setValue(value, cell);
	}

	public void writeValueOnRow(Object value, int index) {
		int cellType = getCellTypeFromValue(value);
		Cell cell = row.createCell(index, cellType);
		setValue(value, cell);
	}

	@Override
	public String[] toArray() {
		String[] result = new String[row.getLastCellNum()];
		for(int i=0; i<result.length; i++) {
			Cell cell = row.getCell(i);
			result[i] = extractValueFromCell(cell);
		}
		return result;
	}
	
	private String evaluateFormula(Cell cell) {
		FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
		CellValue cellValue = evaluator.evaluate(cell);
		switch(cellValue.getCellType()) {
		case Cell.CELL_TYPE_BLANK:
			return null;
		case Cell.CELL_TYPE_BOOLEAN:
			return String.valueOf(cellValue.getBooleanValue());
		case Cell.CELL_TYPE_NUMERIC:
			if (DateUtil.isCellDateFormatted(cell))
				return new DateTime(cell.getDateCellValue()).toString(DateTimeFormat.forPattern(DateTimeFieldMapper.DEFAULT_PATTERN));
			return String.valueOf(cell.getNumericCellValue());
		case Cell.CELL_TYPE_STRING:
			return cell.getRichStringCellValue().toString();
		default :
			throw new RuntimeException("The evaluation formula cannot be extracted from the excel file on row index "+cell.getRowIndex()+" and column index "+cell.getColumnIndex());
		}
	}

	private void setValue(Object value, Cell cell) {
		switch(cell.getCellType()) {
		case Cell.CELL_TYPE_STRING:
			cell.setCellValue(value.toString());
			break;
		case Cell.CELL_TYPE_NUMERIC:
			Number number = (Number)value;
			cell.setCellValue(number.doubleValue());
			break;
		case Cell.CELL_TYPE_BOOLEAN:
			cell.setCellValue((Boolean)value);
			break;
		}
	}

	private int getCellTypeFromValue(Object value) {
		if(value == null || "".equals(value.toString()))
			return Cell.CELL_TYPE_BLANK;
		if(value instanceof Boolean)
			return Cell.CELL_TYPE_BOOLEAN;
		else if(Number.class.isAssignableFrom(value.getClass()))
			return Cell.CELL_TYPE_NUMERIC;
		else
			return Cell.CELL_TYPE_STRING;
	}

	public void duplicate(ExcelRow duplicate) {
		org.apache.poi.ss.usermodel.Row sourceRow = ((ExcelRow) duplicate).row;
		for(int i=0; i<sourceRow.getLastCellNum(); i++) {
			Cell sourceCell = sourceRow.getCell(i);
			if(sourceCell == null)
				continue;
			Cell duplicateCell = row.createCell(i, sourceCell.getCellType());

            switch (sourceCell.getCellType()) {
                case Cell.CELL_TYPE_BOOLEAN:
                    duplicateCell.setCellValue(sourceCell.getBooleanCellValue());
                    break;
                case Cell.CELL_TYPE_NUMERIC:
                	duplicateCell.setCellValue(sourceCell.getNumericCellValue());
                    break;
                case Cell.CELL_TYPE_STRING:
                	duplicateCell.setCellValue(sourceCell.getRichStringCellValue());
                    break;
            }
		}
	}
}
